package it.antonio.ilpizze.func.core;

import it.antonio.ilpizze.func.Mapper;
import it.antonio.ilpizze.func.Order;
import it.antonio.ilpizze.func.Predicate;
import it.antonio.ilpizze.func.Reducer;
import it.antonio.ilpizze.func.Visit;
import it.antonio.ilpizze.func.Worker;
import it.antonio.ilpizze.func.util.InternalUtils;

import java.util.List;
import java.util.RandomAccess;

public abstract class Lists extends Collections implements Core{
	@Override
	public void mustImplementLists() {
	}
	
	public static <T> List<T> add(final List<T> list, T...elems){
		java.util.Collections.addAll(list, elems);
		return list;
	}
	
	public static <T> boolean all(final List<T> List, Predicate<? super T> pred) {
		for(T element: List) {
			if(!pred.match(element)) return false;
		}
		return  true;
	}
	
	public static <T> boolean any(final List<T> List, Predicate<? super T> pred) {
		for(T element: List) {
			if(pred.match(element)) return true;
		}
		return false;
	}
	public static <T> int binarySearch(final List<T> list, T key, Order<? super T> order) {
		return java.util.Collections.binarySearch(list, key, InternalUtils.comparator(order));
	}
	public static  <T> List<T> concat(final List<T> first, final List<T> second) {
		if(first instanceof RandomAccess){
			return InternalUtils.concatRandomAccessList(first, second);
		} else {
			return InternalUtils.concatSequentialList(first, second);
		}
	}
	public static <T> void copy(List<T> src, List<T> dest) {
		java.util.Collections.copy(dest, src);
	}
	public static  <T> List<T> cycle(final List<T> list) {
		if(list instanceof RandomAccess){
			return InternalUtils.cycleRandomAccessList(list);
		} else {
			return InternalUtils.cycleSequentialList(list);
		}
	}
	public static <T> void fill(List<? super T> list, T obj) {
		java.util.Collections.fill(list, obj);
	}
	public static  <T> List<T> filter(final List<T> list, final Predicate<? super T> pred){
		if(list instanceof RandomAccess){
			return InternalUtils.predicateRandomAccessList(list, pred);
		} else {
			return InternalUtils.predicateSequentialList(list, pred);
		}
	}
	public static <T> boolean find(final List<T> List, Predicate<? super T> pred) {
		for(T element: List) {
			if(!pred.match(element)) return false;
		}
		return  true;
	}
	
	public static <T> void forEach(final List<T> list, Worker<? super T> worker) {
		for(T element: list) worker.on(element);
	}
	
	public static <T> int frequency(final List<T> list, T elem){
		return java.util.Collections.frequency(list, elem);
	}
	
	public static  <T,Y> List<Y> map(final List<T> list, final Mapper<? super T, ? extends Y> mapper){
		if(list instanceof RandomAccess){
			return InternalUtils.mappedRandomAccessList(list, mapper);
		} else {
			return InternalUtils.mappedSequentialList(list, mapper);
		}
	}
	public static <T> T max(final List<T> List, Order<T> order) {
		return java.util.Collections.max(List, InternalUtils.comparator(order));
	}
	public static <T> T min(final List<T> List, Order<T> order) {
		return java.util.Collections.min(List, InternalUtils.comparator(order));
	}

	public static <T, R> R reduce(final List<T> list, R start, Reducer<R, T> reducer) {
		R accumulator = start;
		for(T element: list) {
			accumulator = reducer.reduce(accumulator, element);
		}
		return accumulator;
	}

	public static <T> List<T> reverse(final List<T> list) {
		java.util.Collections.reverse(list);
		return list;
	}
	
	public static <T> List<T> rotate(final List<T> list, int dist){
		java.util.Collections.rotate(list, dist);
		return list;
	}
	
	
	public static <T> List<T> shuffle(final List<T> list) {
		java.util.Collections.shuffle(list);
		return list;
	}

	
	public static <T> List<T> sort(final List<T> list, Order<? super T> order) {
		java.util.Collections.sort(list, InternalUtils.comparator(order));
		return list;
	}
	
	public static <T> List<T> swap(final List<T> list, int i, int j) {
		java.util.Collections.swap(list, i,j);
		return list;
	}
	
	
	public static <T> List<T> synchronize(final List<T> list) {
		return java.util.Collections.synchronizedList(list);
	}
	public static <T> List<T> truncate(final List<T> list, final int size) {
		if(list instanceof RandomAccess){
			return InternalUtils.truncateRandomAccessList(list, size);
		} else {
			return InternalUtils.truncateSequentialList(list, size);
		}
	}
	
	public static <T> List<T> unmodifiable(final List<T> list) {
		return java.util.Collections.unmodifiableList(list);
		
	}
	
	public static <T> void visit(final List<T> list, Visit<? super T> visit) {
		for(T element: list) {
			if(visit.on(element) == Visit.STOP) break; 
		}
	}
}
